#import "WebService.h"
#import "XMLReader.h"
#import "ConnectionError.h"

@interface WebService()
@property (nonatomic, strong) NSString* urlString;
@end

@implementation WebService

@synthesize urlString;

+(WebService*)webServiceWithUrl:(NSString*)url {
    return [[WebService alloc] initWithUrl:url];
}

-(id)initWithUrl:(NSString*)url {
    self = [super init];
    if (self) {
        self.urlString = url;
    }
    return self;
}

-(id)sendRequestFor:(NSString*)action withValues:(NSDictionary*)values error:(NSError**) error {
    NSMutableString* stringValues = [[NSMutableString alloc] init];
    if (values) {
        for (NSString* key in values.keyEnumerator) {
            [stringValues appendFormat:@"<%@>%@</%@>", key, [values valueForKey:key], key];
        }
    }
    NSString* soapMessage = [NSString stringWithFormat:
        @"<?xml version='1.0' encoding='utf-8'?>"
         "<soap12:Envelope xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:soap12='http://www.w3.org/2003/05/soap-envelope'>"
            "<soap12:Body>"
                "<%@ xmlns='http://tempuri.org/'>%@</%@>"
            "</soap12:Body>"
         "</soap12:Envelope>", action, stringValues, action];
    NSURL* url = [NSURL URLWithString:self.urlString];
    NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url];
    NSString *msgLength = [NSString stringWithFormat:@"%d", [soapMessage length]];
    [request addValue: @"application/soap+xml; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
    [request addValue: msgLength forHTTPHeaderField:@"Content-Length"];
    [request setHTTPMethod:@"POST"];
    [request setHTTPBody: [soapMessage dataUsingEncoding:NSUTF8StringEncoding]];
    
    NSData* data = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:error];
    NSDictionary* dictionary = [XMLReader dictionaryForXMLData:data];
    NSString* keyPath = [NSString stringWithFormat:@"soap:Envelope.soap:Body.%@Response.%@Result", action, action];
    @try {
        id result = [dictionary valueForKeyPath:keyPath];
        if (result) {
            return result;
        }
        NSString* errorText = [dictionary valueForKeyPath:@"soap:Envelope.soap:Body.soap:Fault.soap:Reason.soap:Text"];
        if (errorText) {
            *error = [NSError errorWithDomain:@"WebService" code:0 userInfo:@{NSLocalizedDescriptionKey:errorText}];
        } else {
            *error = [ConnectionError connectionError];
        }
        return nil;
    }
    @catch (NSException *exception) {
        *error = [NSError errorWithDomain:@"WebService" code:0 userInfo:@{NSLocalizedDescriptionKey:exception.description}];
        return nil;
    }
}

-(id)sendRequestFor:(NSString*)action error:(NSError**)error {
    return [self sendRequestFor:action withValues:nil error:error];
}

- (NSArray*)getModels:(id)response keyName:(NSString *)keyName block:(ConvertFromDictionaryBlock)block {
    if (![response isKindOfClass:[NSDictionary class]]) {
        return @[];
    }
    
    id modelContract = [response objectForKey:keyName];
    if ([modelContract isKindOfClass:[NSArray class]]) {
        NSMutableArray* models = [NSMutableArray arrayWithCapacity:[modelContract count]];
        for (NSDictionary* model in modelContract) {
            [models addObject:block(model)];
        }
        return models;
    }
    
    if (modelContract) {
        return @[block(modelContract)];
    }
    
    return @[];
}

-(NSArray*)getModelsForAction:(NSString*)action withKeyName:(NSString*)keyName convertFromDictionaryBlock:(ConvertFromDictionaryBlock)block error:(NSError**)error {
    id response = [self sendRequestFor:action error:error];
    if (*error) {
        return nil;
    }
    return [self getModels:response keyName:keyName block:block];
}


-(NSArray*)getModelsForAction:(NSString*)action withKeyName:(NSString*)keyName withValues:(NSDictionary*)values convertFromDictionaryBlock:(ConvertFromDictionaryBlock)block error:(NSError**)error {
    id response = [self sendRequestFor:action withValues:values error:error];
    return [self getModels:response keyName:keyName block:block];
}

@end
